import { isArray, isEmptyObject, isString, isUndefined, isObject } from './../utils/validate';
import { Directive, DirectiveBinding } from 'vue';
/** 
 * 自定义权限指令 
 * 
 *  第一个 * 表示角色 ,第二个 * 表示资源，第三个 * 表示具体权限
 * 
 *       三指令用法 针对角色和权限的某一权限
 *  
 *          任意角色都能访问                        *:*:*                                 v-permission.*:*:*="*:*:*"    一般省略,因为这样毫无意义！
 *          具有 admin 角色才能访问                 admin:*:*                             v-permission.admin:*:*="xxx" 
 *          具有 add 权限才能访问                   *:*:add                               v-permission.*:*:add="*:*:xxx"
 *          具有 xxx 资源下任意权限                 *:xxx:*                                v-permission.*:blog:*="*:xxx"
 *          具有 xxx 资源权限同时要有add权限         *:xx:add                              v-permission.admin:blog:add="xxx:yyy:zzz"
 * 
 *       双指令写法，针对角色和资源 ,省略具体权限
 *              
 *            某一个角色具有某一资源下所有权限    v-permission.admin:blog.user:blog        
 * 
 *        单指令写法，省略资源和具体权限
 *            多个角色 连写形式                  v-permission.admin.user.other              admin | user | other  都能访问        
 *                                           
 * 
 * 
 * 对象式写法
 *  v-permission="[{ type: 'serve', role: [], resource: [], permission: [] }, { type: 'expect', role: ['admin'], resource: [], permission: ['add'] }]"
 *  参数说明: type 指定为说明类型，默认可以不指定，第一个式前端接受类型，第二个期望式什么类型
 *                 - 如果指定了类型，两个类型不能一致！，否则会覆盖！
 *                 - expect  期望类型
 *                 - serve   从服务端接受的权限
 *            role: 角色 
 *                 - 如果是一个字符串，可以省略 ["admin"] => "admin"
 *                 - 多个角色使用数组形式 ["admin","user"]
 *                 - 如果省略,默认式为 *
 *            resource: 资源 
 *                 - 同上
 *            permission: 具体权限
 *                 - 同上
 * 
 * 
 * 混合式写法
 * 
 * 参考上面两种方式
 * 只能通过  v-permission.xxxx:yyyy:zzz="{role: aaa, resource: bbbb, permission: cccc }"
 * - role,resource,permission 省略表示 *
 * 
 * 
 * 判断依据
 *    - 如果不指定对应资源默认值，初始化设置为 "*"
 *    - 如果一个资源权限对应设置为 "*",对于期望值来说，可以放行任意权限，对于服务器接收数据来说，表示具有任意权限
 *         - 例如 ["admin:*:*"] === ["*":"*":"add"] = >true
 *         - 例如 ["*:*:add"] ===   ["user":"*":"add"] = >true
 *         - 例如 ["*:*:add"] ===   ["user":"*":"delete"] = >false
 *         - 例如 ["*:account:add"] ===   ["user":"picture":"add"] = >false
 * 
 */




// 所有权限
const ANY_PERMISSION = "*"
// 权限连接符
const PERMISSION_CONNECTION = ":"


type PermssionTypeStr = string | string[] | undefined

/**
 * 权限类型
 */
const RESOLVE_SERVE = 'server'
const RESOLVE_EXPECT = 'expect'
export type ResolvePermssionType = 'expect' | 'server' | undefined

interface Permssion {
    role?: PermssionTypeStr
    resource?: PermssionTypeStr
    permission?: PermssionTypeStr
}


/**
 * 权限对象式写法类型
 */
interface PermssionType extends Permssion {
    type?: ResolvePermssionType
}

const initParams = (obj: Permssion) => {
    if (isUndefined(obj?.role)) {
        obj['role'] = ANY_PERMISSION
    }
    if (isUndefined(obj?.resource)) {
        obj['resource'] = ANY_PERMISSION
    }
    if (isUndefined(obj?.permission)) {
        obj['permission'] = ANY_PERMISSION
    }
}


const compareArrayPermission = (expect: PermssionTypeStr, server: PermssionTypeStr) => {
    if (isArray(expect) && isArray(server)) {
        expect = expect as string[]
        server = server as string[]
        for (let i = 0; i < server.length; i++) {
            // 如果用户具有 任意权限放行
            // 如果资源对任意用户访问，放行
            let index = expect.findIndex(item =>
                (
                    item
                    &&
                    server
                    &&
                    item === (server as string[])[i])
                ||
                item === ANY_PERMISSION
                ||
                (server as string[])[i] === ANY_PERMISSION);
            if (index !== -1) {
                return true
            }
        }
        return false
    }

    if (isArray(expect)) {
        // 如果包含任意权限放行
        if ((expect as string[]).find(e => e && (e as string) === ANY_PERMISSION) || (server as string) === ANY_PERMISSION) {
            return true
        }
        // 从 s2 是否具有s1权限
        return (expect as string[]).indexOf(server as string) > -1
    }

    if (isArray(server)) {
        // 如果包含任意权限放行
        if ((server as string[]).find(e => e && (e as string) === ANY_PERMISSION) || (expect as string) === ANY_PERMISSION) {
            return true
        }
        // 从 s2 是否具有s1权限
        return (server as string[]).indexOf(expect as string) > -1
    }

    return false
}


/**
 * 单个权限比对
 * @param expect 期望权限
 * @param server 服务器接受权限
 * @returns 
 */
const hasPermission = (expect: PermssionTypeStr, server: PermssionTypeStr) => {
    console.log('expect', expect, 'server', server)
    // 如果具有多个权限或者角色
    if ((isArray(expect) || isArray(server))) {
        return compareArrayPermission(expect, server)
    }
    // 如果允许任意权限 或者具有任意权限
    if (expect === ANY_PERMISSION || server === ANY_PERMISSION ) {
        return true
    }
    return expect === server
}

/**
 * 权限比对
 * @param expect 期望权限
 * @param server 接受权限
 * @returns boolean
 */
const hasAllPermission = (expect: Permssion, server: Permssion) => hasPermission(expect?.role, server?.role) && hasPermission(expect?.resource, server?.resource) && hasPermission(expect?.permission, server?.permission)


const pa = (o: PermssionTypeStr): string => {
    if (o && isArray(o)) {
        return [o as string[]].join('|')
    }
    return o as string
}
const permssionStr = (p: Permssion) => `${pa(p.role)}${PERMISSION_CONNECTION}${pa(p.resource)}${PERMISSION_CONNECTION}${pa(p.permission)}`
const printPermission = (expect: Permssion, server: Permssion) => {
    console.log('期望类型', permssionStr(expect), '服务器接受类型', permssionStr(server))
}

const alertPermission = (params1: Permssion, params2: Permssion) => {
    let space = '    '
    let a = {
        '期望类型': space + permssionStr(params1) + space,
        '接受类型': space + permssionStr(params2) + space
    }
    alert(JSON.stringify(a))
}

// 生成 权限对象
const getPermission = (params: string): Permssion => {
    let arr = params.split(PERMISSION_CONNECTION)
    if ((isArray(arr) && arr.length !== 3) || (!isArray(arr))) {
        throw new Error('指令解析失败!')
    }
    let p: Permssion = {
        role: arr[0],
        resource: arr[1],
        permission: arr[2]
    }
    return p
}

// 将字符串转换成 权限字符
const addPermission = (args: PermssionTypeStr): Permssion => {
    if (isString(args)) {
        let i = (args as string).match(new RegExp(PERMISSION_CONNECTION, "g"))
        // 匹配到一个:
        if (isArray(i) && i!.length == 1) {
            args = args + PERMISSION_CONNECTION + ANY_PERMISSION
            // 匹配到两个 ：
        } else if (isArray(i) && i!.length == 2) {

        } else {
            // 一个都没匹配到
            args = `${args}${PERMISSION_CONNECTION}${ANY_PERMISSION}${PERMISSION_CONNECTION}${ANY_PERMISSION}`
        }
        return getPermission(args as string)
    }
    return {} as Permssion
}


// 生成权限
const genRoles = (args: PermssionTypeStr): Permssion[] => {
    if (isString(args)) {
        return [addPermission(args)]
    } else if (isArray(args)) {
        args = args as string[]
        return args.map(arg => addPermission(arg as string));
    } else {
        return [] as Permssion[]
    }
}

// 权限去重
const duplicateRemoval = (p: Permssion[]): Permssion => {
    let roles = [] as string[]
    let resources = [] as string[]
    let permissions = [] as string[]
    let obj: Permssion

    p.forEach(item => {
        roles.push(item['role'] as string)
        resources.push(item['resource'] as string)
        permissions.push(item['permission'] as string)
    })

    // set 去重
    roles = [...new Set([...roles])]
    resources = [...new Set([...resources])]
    permissions = [...new Set([...permissions])]

    // 是否存在最高权限
    roles = roles.find(i => i === ANY_PERMISSION) ? [ANY_PERMISSION] : roles
    resources = resources.find(i => i === ANY_PERMISSION) ? [ANY_PERMISSION] : resources
    permissions = permissions.find(i => i === ANY_PERMISSION) ? [ANY_PERMISSION] : permissions

    obj = {
        role: roles,
        resource: resources,
        permission: permissions
    }
    return obj
}

// 连写形式
const ligature = (el: HTMLElement, binding: DirectiveBinding) => {

    let args = binding.arg
    let modifiers = binding.modifiers
    let values = binding.value

    let expects = [] as Permssion[]
    let servers = [] as Permssion[]

    if (args) {
        // 如果是字符串 比如 "admin" = > "admin:*:*"
        // 如果是数组 比如 ["admin","user","root"] = > ["admin:*:*","user:*:*","root:*:*"]
        expects = [...genRoles(args.split(PERMISSION_CONNECTION))]
    }
    if (modifiers) {
        // 三种形式
        // user
        // user:blog
        // user:blog:add
        expects = [...expects, ...genRoles(Object.keys(modifiers))]
    }


    // 服务器接收值
    // 连写
    let server: Permssion
    if (isArray(values) || isString(values)) {
        values = isArray(values) ? values.join(PERMISSION_CONNECTION) : values
        servers = [...genRoles(values)]
        server = duplicateRemoval(servers)
        console.log('连写式写法', server)
        // 对象式写法
    } else if (isObject(values)) {
        initParams(values as Permssion)
        server = values
        console.log('对象式写法', server)
    }

    // 权限去重与合并
    let expect: Permssion = duplicateRemoval(expects)

    // 判断权限

    Promise.resolve().then(() => {
        printPermission(expect, server)
        alertPermission(expect, server)
        if (!hasAllPermission(expect, server)) {
            el.style.display = 'none'
            return;
        }
    })

}



// 对象式写法
const bindingValueIsObject = (el: HTMLElement, binding: DirectiveBinding) => {
    const values = binding.value
    if (!isArray(values) || values.length !== 2) {
        // throw new Error('v-permission对象式必须式两个对象类型！')
        console.warn('v-permission对象式必须式两个对象类型！')
        return;
    }
    let [params1, params2] = values as PermssionType[]
    // console.log(params1, params2, !isUndefined(params1?.type), !isUndefined(params2?.type))
    if (!isUndefined(params1?.type) && !isUndefined(params2?.type) && params1.type === params2.type) {
        console.warn('两个类型不能一致！')
        return;
    }

    // 两个参数均为 undefined
    if (isUndefined(params1?.type) && isUndefined(params2?.type)) {
        params1.type = RESOLVE_SERVE
        params2.type = RESOLVE_EXPECT
    }
    // 参数一为 undefined
    else if (isUndefined(params1?.type) && params2?.type) {
        params2.type === RESOLVE_SERVE ? params1.type = RESOLVE_EXPECT : params1.type = RESOLVE_SERVE
    }
    // 参数二为 undefined
    else if (isUndefined(params2?.type) && params1?.type) {
        params1.type === RESOLVE_SERVE ? params2.type = RESOLVE_EXPECT : params2.type = RESOLVE_SERVE
    }

    // 参数初始化
    initParams(params1)
    initParams(params2)

    // 交换，保证 期望值在前
    console.log("before", params1.type, params2.type)
    if (params1.type === 'server') {
        let temp: PermssionType
        temp = params1
        params1 = params2
        params2 = temp
    }
    console.log("after", params1.type, params2.type)

    alertPermission(params1, params2)
    printPermission(params1, params2)
    if (!hasAllPermission(params1, params2)) {
        el.style.display = 'none'
        console.log('我需要隐藏了哦！')
    }


}


/**
 * 指令
 */
const vPermission: Directive = {
    // 或事件监听器应用前调用
    created(el: HTMLElement, binding: DirectiveBinding) {
        // 下面会介绍各个参数的细节
        const arg = binding.arg
        const modifiers = binding.modifiers
        const value = binding.value
        if (isUndefined(value)) {
            console.log('value is undefined ')
            return;
        }
        if (isEmptyObject(value)) {
            console.log('value is object but not any attr and value ')
            return;
        }
        if (isArray(value) && value == 0) {
            console.log('value is array but length equal 0 ')
            return;
        }
        // 获取参数个数
        el.addEventListener('click', () => {
            console.log('======================')
            console.log('arg', arg)
            console.log('modifiers', modifiers)
            console.log('value', value)
            console.log('======================')
            // 判断写法
            // 对象式写法
            if (!arg && isEmptyObject(modifiers)) {
                bindingValueIsObject(el, binding)
            }
            // 连写式写法 | 混合式写法
            else if (arg || !isEmptyObject(modifiers)) {
                ligature(el, binding)
            }
            else {
                console.log('绑定错误')
            }
        })
    },
}

const permission = {
    name: 'permission',
    directive: vPermission
}

export default permission
